<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>星级评分之完善程序</title>
    <style>
      body, ul, li {
        padding: 0;
        margin: 0;
      }
      li {
        list-style: none;
      }
      .rating {
        width: 190px;
        height: 32px;
        margin: 100px auto;
      }
      .rating-item {
        display: inline-block;
        width: 32px;
        height: 32px;
        background: url(img/star.png) no-repeat;
        cursor: pointer;
      }
    </style>
    <script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
  </head>
  <body>
    <ul class="rating" id="rating">
      <li class="rating-item" title="十分不好"></li>
      <li class="rating-item" title="不好"></li>
      <li class="rating-item" title="一般"></li>
      <li class="rating-item" title="好"></li>
      <li class="rating-item" title="很好"></li>
    </ul>

    <ul class="rating" id="ratingOne">
      <li class="rating-item" title="十分不好"></li>
      <li class="rating-item" title="不好"></li>
      <li class="rating-item" title="一般"></li>
      <li class="rating-item" title="好"></li>
      <li class="rating-item" title="很好"></li>
    </ul>

    <ul class="rating" id="ratingTwo">
      <li class="rating-item" title="十分不好"></li>
      <li class="rating-item" title="不好"></li>
      <li class="rating-item" title="一般"></li>
      <li class="rating-item" title="好"></li>
      <li class="rating-item" title="很好"></li>
    </ul>
    <script>
      var rating = (function() {
        // 继承
        var extend = function(subClass, superClass) {
          var F = function() {};
          F.prototype = superClass.prototype;
          subClass.prototype = new F();
          subClass.prototype.constructor = subClass;
        };
        // 点亮星星的父类
        var Light = function(el, options) {
          this.$el = $(el);
          this.$item = this.$el.find('.rating-item');
          this.opts = options;
          this.add = 1;
          this.selectEvent = 'mouseover';
        };
        Light.prototype.init = function() {
          this.lightOn(this.opts.num);
          if(!this.opts.readOnly) {
            this.bindEvent();
          }
        };
        Light.prototype.lightOn = function(num) {
          num = parseInt(num);
          this.$item.each(function(index) {
            if(index < num) {
              $(this).css('background-position', '0 -40px');
            } else {
              $(this).css('background-position', '0 0');
            }
          });
        };
        Light.prototype.bindEvent = function(e) {
          var self = this;
          var itemLength = self.$item.length;
          self.$el.on(self.selectEvent, '.rating-item', function(e) {
            var $this = $(this);
            var num = 0;
            self.select(e, $this);
            num = $(this).index() + self.add;
            self.lightOn(num);
            (typeof self.opts.select === 'function') && self.opts.select.call(this, num, itemLength);
            self.$el.trigger('select', [num, itemLength]);
          }).on('click', '.rating-item', function() {
            self.opts.num = $(this).index() + self.add;
            (typeof self.opts.chosen === 'function') && self.opts.chosen.call(this, self.opts.num, itemLength);
            self.$el.trigger('chosen', [self.opts.num, itemLength]);
          }).on('mouseout', function() {
            self.lightOn(self.opts.num);
          });
        };
        Light.prototype.select = function() {
          throw new Error('子类必须重写此方法');
        }
        Light.prototype.unbindEvent = function() {
          this.$el.off();
        };

        // 点亮整颗星星
        var LightEntire = function(el, options) {
          // call的作用是让this强制绑定LightEntire对象
          Light.call(this, el, options);
          this.selectEvent = 'mouseover';
        };
        extend(LightEntire, Light);
        LightEntire.prototype.lightOn = function(num) {
          Light.prototype.lightOn.call(this, num);
        };
        LightEntire.prototype.select = function() {
          self.add = 1;
        };

        // 点亮半颗星星
        var LightHalf = function(el, options) {
          Light.call(this, el, options);
          this.selectEvent = 'mousemove';
        };
        extend(LightHalf, Light);
        LightHalf.prototype.lightOn = function(num) {
          var count = parseInt(num);
          var isHalf = count !== num;
          Light.prototype.lightOn.call(this, count);
          if(isHalf) {
            // 获取当前的星星位置，让它变成半星
            this.$item.eq(count).css('background-position', '0 -80px')
          }
        };
        LightHalf.prototype.select = function(e, $this) {
          if(e.pageX - $this.offset().left < $this.width() / 2) {
            this.add = 0.5;
          } else {
            this.add = 1;
          }
        };

        // 默认参数
        var defaults = {
          mode: 'LightEntire',
          num: 0,
          readOnly: false,
          select: function() {},
          chosen: function() {}
        };

        var mode = {
          'LightEntire': LightEntire,
          'LightHalf': LightHalf
        }

        // 创建初始方法
        var init = function(el, option) {
          var $el = $(el);
          var rating = $el.data('rating');
          var options = $.extend({}, defaults, typeof option === 'object' && option);
          if(!mode[options.mode]) {
            options.mode = 'LightEntire';
          }
          if(!rating) {
            $el.data('rating', (rating = new mode[options.mode](el, options)));
            rating.init();
          }
          if(typeof option === 'string') {
            rating[option]();
          }
        };

        // jQuery插件
        $.fn.extend({
          rating: function(option) {
            return this.each(function() {
              init(this, option);
            });
          }
        });

        return {
          initfn: init
        };
      })();

      // 单击事件之后解绑一切事件
      rating.initfn('#rating', {
        mode: 'LightEntire',
        num: 2,
        chosen: function() {
          rating.initfn('#rating', 'unbindEvent');
        }
      });

      // 插件式调用
      $('#ratingOne').rating({
        mode: 'LightHalf',
        num: 2.5
      });
      $('#ratingOne').on('chosen', function() {
        $('#ratingOne').rating('unbindEvent');
      })

      // 单击事件之后不解绑一切事件
      rating.initfn('#ratingTwo', {
        mode: 'LightEntire',
        num: 1
      });
    </script>
  </body>
</html>